from __future__ import annotations

import platform
import threading
import time
from typing import Callable, Dict, List, Tuple

# 仅在 Windows 下导入 winsound，避免非 Windows 崩溃
if platform.system() == "Windows":
    import winsound  # type: ignore


UpdateCallback = Callable[[float], None]
CompleteCallback = Callable[[], None]


class Timer:
    """精确计时器核心类（正/倒计时）"""

    def __init__(self, root) -> None:
        self.root = root
        self.running: bool = False
        self.paused: bool = False
        self.elapsed_seconds: float = 0.0
        self._callbacks: Dict[str, Callable] = {}
        self._lock = threading.Lock()

        # 暂停时间累计
        self.pause_start: float = 0.0
        self.pause_duration: float = 0.0
        self.start_time: float = 0.0

    def set_callbacks(
        self, update_cb: UpdateCallback, complete_cb: CompleteCallback
    ) -> None:
        self._callbacks["update"] = update_cb
        self._callbacks["complete"] = complete_cb

    def _safe_after(self, cb: Callable, *args) -> None:
        # root 可能在关闭过程中被销毁
        if getattr(self.root, "winfo_exists", lambda: 0)():
            self.root.after(0, cb, *args)

    def start(self) -> None:
        """启动正计时"""
        with self._lock:
            if self.running:
                return
            self.running = True
            self.paused = False
            self.pause_duration = 0.0
            self.start_time = time.perf_counter()
            threading.Thread(target=self._count_up, daemon=True).start()

    def _count_up(self) -> None:
        while True:
            with self._lock:
                if not self.running:
                    break
                if self.paused:
                    pass  # 仍保持循环，但不更新
                else:
                    now = time.perf_counter()
                    self.elapsed_seconds = now - self.start_time - self.pause_duration
                    if cb := self._callbacks.get("update"):
                        self._safe_after(cb, self.elapsed_seconds)
            time.sleep(0.1)

    def countdown(self, timers: List[Tuple[int, int, int]]) -> None:
        """倒计时 (支持多个片段依次执行)"""

        def _run():
            for h, m, s in timers:
                with self._lock:
                    if not self.running:
                        break
                    total = h * 3600 + m * 60 + s
                    if cb := self._callbacks.get("update"):
                        self._safe_after(cb, float(total))
                start = time.perf_counter()
                paused_total = 0.0
                while True:
                    with self._lock:
                        if not self.running:
                            return
                        if self.paused:
                            ps = time.perf_counter()
                        else:
                            remaining = total - (
                                time.perf_counter() - start - paused_total
                            )
                            if remaining <= 0:
                                break
                            if cb := self._callbacks.get("update"):
                                self._safe_after(cb, remaining)
                    # 等待/暂停处理
                    time.sleep(0.1)
                    if self.paused:
                        while True:
                            time.sleep(0.1)
                            with self._lock:
                                if not self.running:
                                    return
                                if not self.paused:
                                    paused_total += time.perf_counter() - ps
                                    break
                self._play_alert()

            with self._lock:
                self.running = False
            if cb := self._callbacks.get("complete"):
                self._safe_after(cb)

        with self._lock:
            self.running = True
            self.paused = False
            self.pause_duration = 0.0
        threading.Thread(target=_run, daemon=True).start()

    def _play_alert(self) -> None:
        try:
            if platform.system() == "Windows":  # pragma: no cover
                winsound.Beep(1000, 500)  # type: ignore
        except Exception as e:
            print(f"提示音错误: {e}")

    def pause(self) -> None:
        with self._lock:
            if self.running and not self.paused:
                self.paused = True
                self.pause_start = time.perf_counter()

    def resume(self) -> None:
        with self._lock:
            if self.running and self.paused:
                self.paused = False
                self.pause_duration += time.perf_counter() - self.pause_start

    def stop(self) -> None:
        with self._lock:
            if not self.running and not self.paused:
                return
            self.running = False
            self.paused = False
            self.elapsed_seconds = 0.0
            self.pause_duration = 0.0
        if cb := self._callbacks.get("complete"):
            self._safe_after(cb)
